from fastapi import FastAPI
from pydantic import BaseModel
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlencode
from requests.utils import dict_from_cookiejar
import base64
from fastapi.responses import FileResponse
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
import json
import re
from urllib.parse import urlparse

app = FastAPI()

class LoginCredentials(BaseModel):
    username: str
    password: str

class AESUtil:
    @staticmethod
    def aes_encrypt(key_base64, plaintext):
        key = base64.b64decode(key_base64)
        cipher = AES.new(key, AES.MODE_ECB)
        padded_text = pad(plaintext.encode('utf-8'), AES.block_size)
        encrypted_text = cipher.encrypt(padded_text)
        return base64.b64encode(encrypted_text).decode('utf-8')

    @staticmethod
    def aes_decrypt(key_base64, encrypted_base64):
        key = base64.b64decode(key_base64)
        cipher = AES.new(key, AES.MODE_ECB)
        encrypted_text = base64.b64decode(encrypted_base64)
        decrypted_text = cipher.decrypt(encrypted_text)
        return unpad(decrypted_text, AES.block_size).decode('utf-8')


def get_login_data():
    url = "https://sso.qlu.edu.cn/login?service=https:%2F%2Flibyuyue.qlu.edu.cn%2Fv4%2Flogin%2Fcas"

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1',
        'Sec-Fetch-Dest': 'document',
        'Sec-Fetch-Mode': 'navigate',
        'Sec-Fetch-Site': 'none',
        'Sec-Fetch-User': '?1',
        'Priority': 'u=0, i',
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()

        soup = BeautifulSoup(response.text, 'html.parser')

        login_crypto = soup.find('p', id='login-croypto')
        login_page_flowkey = soup.find('p', id='login-page-flowkey')

        cookies_string = '; '.join([f"{name}={value}" for name, value in response.cookies.items()])

        return {
            "login_crypto": login_crypto.text if login_crypto else None,
            "login_page_flowkey": login_page_flowkey.text if login_page_flowkey else None,
            "cookies": cookies_string,
        }

    except Exception:
        return None


def login_request(username, execution, croypto, password, cookies):
    url = "https://sso.qlu.edu.cn/login"

    payload = {
        'username': username,
        'type': 'UsernamePassword',
        '_eventId': 'submit',
        'geolocation': '',
        'execution': execution,
        'croypto': croypto,
        'password': password
    }

    payload_str = urlencode(payload)

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
        'Referer': 'https://sso.qlu.edu.cn/login?service=https:%2F%2Flibyuyue.qlu.edu.cn%2Fv4%2Flogin%2Fcas',
        'Origin': 'https://sso.qlu.edu.cn',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1',
        'Sec-Fetch-Dest': 'document',
        'Sec-Fetch-Mode': 'navigate',
        'Sec-Fetch-Site': 'same-origin',
        'Sec-Fetch-User': '?1',
        'Priority': 'u=0, i',
        'Cookie': cookies,
        'Content-Type': 'application/x-www-form-urlencoded'
    }

    # 创建一个会话以跟踪所有重定向和 cookies
    session = requests.Session()
    
    try:
        response = session.post(
            url, 
            headers=headers, 
            data=payload_str, 
            allow_redirects=True
        )
        
        # 获取会话中所有的 cookies
        all_cookies = session.cookies.get_dict()
        cookies_text = "; ".join(f"{key}={value}" for key, value in all_cookies.items())
        
        # 获取最终的 URL，提取 CAS 值
        final_url = response.url
        
        # 尝试从 URL 中提取 CAS 值
        cas_value = None
        
        # 从 URL 中提取 CAS
        if "cas=" in final_url:
            cas_value = final_url.split("cas=")[-1].split("&")[0]
        elif parsed_url := urlparse(final_url):
            if parsed_url.fragment and "cas=" in parsed_url.fragment:
                fragment_params = dict(item.split("=") for item in parsed_url.fragment.split("/")[-1].split("&"))
                cas_value = fragment_params.get("cas")
        
        # 如果 URL 中没找到，尝试从响应中提取
        if not cas_value and "cas" in response.text.lower():
            cas_matches = re.findall(r'cas[\s=:"\']+([a-f0-9]+)', response.text.lower())
            if cas_matches:
                cas_value = cas_matches[0]
        
        return {
            "cas_value": cas_value,
            "cookies": cookies_text,
            "final_url": final_url,
            "status_code": response.status_code
        }
        
    except Exception:
        return {
            "cas_value": None,
            "cookies": {},
            "error": "登录请求失败",
            "final_url": None,
            "status_code": None
        }


def get_user_token(cas_value, cookies):
    url = 'https://libyuyue.qlu.edu.cn/v4/login/user'
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0',
        'Accept': 'application/json, text/plain, */*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Content-Type': 'application/json',
        'sec-ch-ua-platform': '"Windows"',
        'X-Requested-With': 'XMLHttpRequest',
        'sec-ch-ua': '"Chromium";v="134", "Not:A-Brand";v="24", "Microsoft Edge";v="134"',
        'sec-ch-ua-mobile': '?0',
        'Origin': 'https://libyuyue.qlu.edu.cn',
        'Sec-Fetch-Site': 'same-origin',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Dest': 'empty',
        'Referer': 'https://libyuyue.qlu.edu.cn/h5/index.html',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,pl;q=0.5',
        'Cookie': cookies,
        'Connection': 'close'
    }
    
    payload = {
        'cas': cas_value
    }
    
    try:
        # 创建一个新的会话用于控制 HTTP 版本
        session = requests.Session()
        # 禁用连接池，强制每次请求使用新连接，间接实现 HTTP/1.1
        session.mount('https://', requests.adapters.HTTPAdapter(pool_connections=0, pool_maxsize=0))
        
        response = session.post(
            url,
            headers=headers,
            json=payload
        )
        
        response_text = response.text
        
        try:
            # 处理 UTF-8 BOM
            if response_text.startswith('\ufeff'):
                response_text = response_text[1:]
            
            # 解析 JSON 响应
            json_data = json.loads(response_text)
            
            # 检查并提取 token
            if json_data.get('code') == 0 and 'data' in json_data:
                data = json_data['data']
                
                # 检查字段名称
                member_data = None
                if 'member' in data:
                    member_data = data['member']
                elif 'membeer' in data:
                    member_data = data['membeer']
                
                if member_data and 'token' in member_data:
                    token = member_data.get('token', '')
                    
                    return {
                        'success': True,
                        'token': token,
                        'user_info': {
                            'id': member_data.get('id', ''),
                            'name': member_data.get('name', ''),
                            'card': member_data.get('card', ''),
                            'roleName': member_data.get('roleName', ''),
                            'deptName': member_data.get('deptName', '')
                        }
                    }
            
            return {
                'success': False,
                'message': f"获取 token 失败: {json_data.get('message', '未知错误')}",
                'response_data': json_data
            }
            
        except ValueError:
            # 尝试手动提取 token (应急措施)
            token_match = re.search(r'"token":"([^"]+)"', response_text)
            if token_match:
                token = token_match.group(1)
                
                # 尝试提取其他用户信息
                id_match = re.search(r'"id":"([^"]+)"', response_text)
                name_match = re.search(r'"name":"([^"]+)"', response_text)
                card_match = re.search(r'"card":"([^"]+)"', response_text)
                role_match = re.search(r'"roleName":"([^"]+)"', response_text)
                dept_match = re.search(r'"deptName":"([^"]+)"', response_text)
                
                return {
                    'success': True,
                    'token': token,
                    'user_info': {
                        'id': id_match.group(1) if id_match else '',
                        'name': name_match.group(1) if name_match else '',
                        'card': card_match.group(1) if card_match else '',
                        'roleName': role_match.group(1) if role_match else '',
                        'deptName': dept_match.group(1) if dept_match else ''
                    }
                }
            
            return {
                'success': False,
                'message': "获取 token 失败: 服务器响应不是有效的 JSON"
            }
            
    except Exception as e:
        return {
            'success': False,
            'message': f"获取 token 时出错: {str(e)}"
        }


@app.post("/login")
async def login_and_get_token(credentials: LoginCredentials):
    try:
        login_data = get_login_data()
        if not login_data:
            return {"success": False, "message": "获取登录数据失败"}
        
        encrypted_password = AESUtil.aes_encrypt(login_data["login_crypto"], credentials.password)
        
        login_result = login_request(credentials.username, login_data["login_page_flowkey"], 
                               login_data["login_crypto"], encrypted_password, login_data["cookies"])
        
        # 检查是否成功获取了 CAS 值或 Cookies
        if not login_result["cas_value"] and not (login_result["cookies"] and len(login_result["cookies"]) > 0):
            return {"success": False, "message": "登录失败，未获取到有效认证信息"}
        
        # 获取用户 token
        token_result = get_user_token(login_result["cas_value"], login_result["cookies"])
        
        if not token_result['success']:
            return {
                "success": False,
                "message": f"获取 token 失败: {token_result.get('message', '未知错误')}"
            }
        
        return {
            "success": True, 
            "token": token_result['token'],
            "user_info": token_result['user_info']
        }
    
    except Exception as e:
        return {"success": False, "message": f"登录过程出错: {str(e)}"}


@app.get("/config")
async def read_config():
    return FileResponse("config.html")


@app.get("/")
async def read_config():
    return FileResponse("index.html")


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=6666) 